/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.icanci.loopstack.damc.core.dynamic;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import com.google.common.collect.Sets;

import cn.icanci.loopstack.damc.cache.ArgsValidateResult;
import cn.icanci.loopstack.damc.cache.DamcCenterConfigRepository;
import cn.icanci.loopstack.damc.cache.EnvEnum;

@SuppressWarnings("all")
public class DynamicAgent {
    /**
     * Instrumentation
     */
    private static Instrumentation instrumentation;

    /**
     * JDK Agent premain 
     *
    
     * @param args args，例如: 127.0.0.1;127.0.0.2|9191|9292|QA|DAMC测试项目|DAMC-TEST-CORE^/Users/icanci/ideaProjects/loopstack/damc/damc-core/target/damc-core-1.0-SNAPSHOT-jar-with-dependencies.jar
     * @param inst inst
     */
    public static void premain(java.lang.String args, Instrumentation inst) {
        if (StringUtils.isBlank(args)) {
            return;
        }

        java.lang.String[] argsArray = args.split("\\^");
        if (argsArray.length != 2) {
            return;
        }

        // 加载到系统参数里面
        System.getProperties().put(DamcCoreConstant.DAMC_ARGS_KEY, args);
        System.getProperties().put(DamcCoreConstant.DAMC_ARGS_INIT_KEY, argsArray[0]);
        System.getProperties().put(DamcCoreConstant.DAMC_ARGS_AGENT_PATH_KEY, argsArray[1]);

        // doAgentTransformer
        doAgentTransformer(inst, argsArray[0]);
    }

    // 动态 Attach 时加载 Agent 的入口方法
    public static void agentmain(java.lang.String args, Instrumentation inst) {
        // doAgentTransformer
        doAgentTransformer(inst, java.lang.String.valueOf(System.getProperties().get(DamcCoreConstant.DAMC_ARGS_INIT_KEY)));
    }

    public static Instrumentation getInstrumentation() {
        return instrumentation;
    }

    public static void setInstrumentation(Instrumentation instrumentation) {
        DynamicAgent.instrumentation = instrumentation;
        DynamicAgent.instrumentation.addTransformer(new DynamicTransformer(), true);
    }

    public static void setResult(ArgsValidateResult result) {
        DamcCenterConfigRepository.setArgsValidateResult(result);
    }

    /**
     * 是否完成了加载
     * 
     * @return 返回是否完成了加载
     */
    public static boolean inited() {
        return getInstrumentation() != null && DamcCenterConfigRepository.inited();
    }

    /**
     * doAgentTransformer
     * 
     * @param inst inst
     * @param argsArray argsArray
     */
    private static void doAgentTransformer(Instrumentation inst, java.lang.String args) {
        try {
            // ArgsValidateResult
            ArgsValidateResult result = getArgsValidate(args);
            if (result == null) {
                return;
            }
            DamcCenterConfigRepository.setArgsValidateResult(result);
            // instrumentation
            instrumentation = inst;
            // 注册一个可重用的 Transformer
            inst.addTransformer(new DynamicTransformer(), true);

            // ======================================== 运行时处理  ========================================
            // 获取项目中的目标ClassLoader 和 这个进行匹配
            Object appRunnerClassLoaderName = System.getProperties().get(DamcCoreConstant.APP_RUNNER_CLASS_LOADER);
            if (appRunnerClassLoaderName == null) {
                return;
            }

            java.lang.String className = java.lang.String.valueOf(appRunnerClassLoaderName);
            if (StringUtils.isEmpty(className)) {
                return;
            }
            Set<ClassLoader> classLoaders = listClassLoaders();
            for (ClassLoader clazzLoader : classLoaders) {
                if (clazzLoader == null) {
                    continue;
                }
                if (!StringUtils.equals(clazzLoader.getClass().getName(), className)) {
                    continue;
                }

                Class<?> damcCoreStartClazz = clazzLoader.loadClass("cn.icanci.loopstack.damc.core.DamcCoreStart");
                Object start = damcCoreStartClazz.newInstance();
                Class<?> argsValidateResultClazz = clazzLoader.loadClass("cn.icanci.loopstack.damc.cache.ArgsValidateResult");
                Object startResult = argsValidateResultClazz.newInstance();

                Field[] fields = startResult.getClass().getDeclaredFields();
                for (Field field : fields) {
                    if (StringUtils.equals(field.getName(), "damcCenterAddress")) {
                        field.setAccessible(true);
                        field.set(startResult, result.getDamcCenterAddress());
                    }
                    if (StringUtils.equals(field.getName(), "damcCenterPort")) {
                        field.setAccessible(true);
                        field.set(startResult, result.getDamcCenterPort());
                    }
                    if (StringUtils.equals(field.getName(), "mockedProjectPort")) {
                        field.setAccessible(true);
                        field.set(startResult, result.getMockedProjectPort());
                    }
                    if (StringUtils.equals(field.getName(), "mockedProjectEnv")) {
                        field.setAccessible(true);
                        field.set(startResult, result.getMockedProjectEnv());
                    }
                    if (StringUtils.equals(field.getName(), "mockedProjectName")) {
                        field.setAccessible(true);
                        field.set(startResult, result.getMockedProjectName());
                    }
                    if (StringUtils.equals(field.getName(), "mockedProjectCode")) {
                        field.setAccessible(true);
                        field.set(startResult, result.getMockedProjectCode());
                    }

                }

                Method[] methods = start.getClass().getMethods();
                for (Method method : methods) {
                    if (StringUtils.equals(method.getName(), "setInitParam")) {
                        method.invoke(start, startResult, inst);
                    }
                }
            }
        } catch (Exception e) {
            // TODO logs
            System.out.println(e);
        }
    }

    public static Set<ClassLoader> listClassLoaders() {
        if (instrumentation == null) {
            System.out.println("Instrumentation is not initialized!");
            return Sets.newHashSet();
        }

        // 获取所有已加载的类
        Set<ClassLoader> classLoaders = Sets.newHashSet();
        for (Class<?> clazz : instrumentation.getAllLoadedClasses()) {
            ClassLoader classLoader = clazz.getClassLoader();
            classLoaders.add(classLoader);
        }
        return classLoaders;
    }

    /**
     * 解析ArgsResult
     * 127.0.0.1;127.0.0.2|9191|9292|DAMC测试项目|DAMC-TEST-CORE|QA
     * 
     * @param args args
     * @return ArgsValidateResult
     */
    private static ArgsValidateResult getArgsValidate(java.lang.String args) {
        // 启动参数为空，不处理
        if (StringUtils.isEmpty(args)) {
            return null;
        }

        String[] arg = args.split("\\|");
        if (arg.length != 6) {
            return null;
        }

        // 127.0.0.1;127.0.0.2
        java.lang.String damcCenterAddress = arg[0];
        // 9191
        if (!NumberUtils.isNumber(arg[1])) {
            return null;
        }
        int damcCenterPort = Integer.parseInt(arg[1]);
        // 9292
        if (!NumberUtils.isNumber(arg[2])) {
            return null;
        }
        int mockedProjectPort = Integer.parseInt(arg[2]);

        // QA
        EnvEnum mockedProjectEnv = EnvEnum.getByCode(arg[3]);
        if (mockedProjectEnv == null) {
            return null;
        }

        String mockedProjectName = arg[4];
        if (StringUtils.isBlank(mockedProjectName)) {
            return null;
        }

        java.lang.String mockedProjectCode = arg[5];
        if (StringUtils.isBlank(mockedProjectCode)) {
            return null;
        }

        return new ArgsValidateResult(damcCenterAddress, damcCenterPort, mockedProjectPort, mockedProjectEnv.getEnvCode(), mockedProjectName, mockedProjectCode);
    }

    /**
     * 提供一个外部触发的入口，用来进行动态刷新
     *
     * @param targetClass 目标需要刷新的classes
     */
    public static void reTransform(Class<?> targetClass) {
        try {
            if (instrumentation != null) {
                instrumentation.retransformClasses(targetClass);
            }
        } catch (Throwable e) {
            System.out.println(e.getMessage());
        }
    }

    /**
     * DynamicTransformer
     */
    static class DynamicTransformer implements ClassFileTransformer {
        @Override
        public byte[] transform(ClassLoader loader, java.lang.String clazzName, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
            return DynamicAgentClassCache.doTransform(classBeingRedefined, classfileBuffer);
        }
    }

}
